package service

import (
	"crypto/md5"
	"dc-news/common/global/consts"
	"dc-news/common/global/setting"
	"dc-news/common/resp"
	"dc-news/common/utils"
	"dc-news/common/utils/email"
	myredis "dc-news/common/utils/redis"
	"dc-news/common/utils/upyun"
	"dc-news/dao"
	"dc-news/entity"
	"dc-news/vo"
	"encoding/hex"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis"
	"go.uber.org/zap"
	"golang.org/x/crypto/bcrypt"
	"io"
	"net/http"
	"os"
	"path"
	"time"
)

// LoginByUsernameService 通过账号登录
func LoginByUsernameService(c *gin.Context, req *vo.LoginReq) {
	user, err := dao.GetUserByUsername(req.Username)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	if user == nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.FailCode, "用户名或密码错误"))
		return
	}
	err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.FailCode, "用户名或密码错误"))
		return
	}
	channels, err := dao.GetChannelsByUserId(user.ID)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	token, err := utils.GenerateToken(user, channels)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	c.JSON(http.StatusOK, vo.NewLoginResp(user, token))
}

// LoginByEmailService 通过邮箱登录
func LoginByEmailService(c *gin.Context, req *vo.LoginReq) {
	user, err := dao.GetUserByEmail(req.Username)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	if user == nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.FailCode, "用户名或密码错误"))
		return
	}
	err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.FailCode, "用户名或密码错误"))
		return
	}
	channels, err := dao.GetChannelsByUserId(user.ID)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	token, err := utils.GenerateToken(user, channels)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	c.JSON(http.StatusOK, vo.NewLoginResp(user, token))
}

// ResetPasswordEmailService 发送重置密码邮件
func ResetPasswordEmailService(c *gin.Context, req *vo.ResetPasswordEmailReq) {
	var user *entity.User
	var err error
	if req.Email != "" {
		user, err = dao.GetUserByEmail(req.Email)

	} else {
		user, err = dao.GetUserByUsername(req.Username)
	}
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	if user == nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.FailCode, "用户不存在"))
		return
	}
	token := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s%d", user.Email, time.Now().Unix()))))
	ok := email.SendResetPasswordEmail(user, token)
	if !ok {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		return
	}
	myredis.Client.Set(fmt.Sprintf("pwdreset:%s", token), user.Email, time.Minute*30)
	c.JSON(http.StatusOK, resp.NewOkResp())
}

// ResetPasswordService 重置密码
func ResetPasswordService(c *gin.Context, req *vo.ResetPasswordReq) {
	v, err := myredis.Client.Get(fmt.Sprintf("pwdreset:%s", req.Token)).Result()
	if err != nil {
		if err == redis.Nil {
			c.JSON(http.StatusOK, resp.NewErrResp(consts.FailCode, "token已失效或不存在"))
			return
		} else {
			c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
			zap.L().Error(err.Error())
			return
		}
	}
	user, _ := dao.GetUserByEmail(req.Email)
	if user.Email != v {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.FailCode, "token不正确"))
		return
	}
	hashBytes, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
	hashStr := string(hashBytes)
	err = dao.ChangePasswordById(user.ID, hashStr)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	myredis.Client.Del(fmt.Sprintf("pwdreset:%s", req.Token))
	c.JSON(http.StatusOK, resp.NewOkResp())

}

// RegisterService 注册
func RegisterService(c *gin.Context, req *vo.RegisterReq) {
	exist := dao.CheckUsernameAndEmail(req.Username, req.Email)
	if exist {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.FailCode, "用户名或邮箱已存在"))
		return
	}
	hashBytes, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
	hashStr := string(hashBytes)
	user := &entity.User{
		Username: req.Username,
		Password: hashStr,
		Email:    req.Email,
		Avatar:   fmt.Sprintf("%s/img/avatar/avatar.jpg", setting.Conf.CDNDomain),
	}
	err := dao.CreateUser(user)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	token := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s%d", user.Email, time.Now().Unix()))))
	myredis.Client.Set(fmt.Sprintf("confirm:%s", token), user.Email, time.Minute*30)
	email.SentConfirmEmail(user, token)
	c.JSON(http.StatusOK, vo.NewRegisterResp(user.ID))
}

// ConfirmEmailService 确认邮件
func ConfirmEmailService(c *gin.Context, req *vo.ConfirmEmailReq) {
	v, err := myredis.Client.Get(fmt.Sprintf("confirm:%s", req.Token)).Result()
	if err != nil {
		if err == redis.Nil {
			c.JSON(http.StatusOK, resp.NewErrResp(consts.FailCode, "token已失效或不存在"))
			return
		} else {
			c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
			zap.L().Error(err.Error())
			return
		}
	}
	user, _ := dao.GetUserByEmail(req.Email)
	if user.Email != v {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.FailCode, "token不正确"))
		return
	}
	err = dao.ConfirmEmailByUserId(user.ID)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	myredis.Client.Del(fmt.Sprintf("confirm:%s", req.Token))
	c.JSON(http.StatusOK, resp.NewOkResp())
}

// GetUserInfoService 获取用户信息
func GetUserInfoService(c *gin.Context, id int) {
	user, err := dao.GetUserById(id)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	c.JSON(http.StatusOK, vo.NewGetUserInfoResp(user))
}

// GetUserSettingService 获取用户设置
func GetUserSettingService(c *gin.Context, id int) {
	userSetting, err := dao.GetUserSettingById(id)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	c.JSON(http.StatusOK, vo.NewGetUserSettingResp(userSetting))
}

// UpdateUserSettingService 更新用户信息
func UpdateUserSettingService(c *gin.Context, id int, req *vo.UpdateUserSettingReq) {
	err := dao.UpdateUserSettingById(id, &entity.UserSetting{
		ThemeColor: req.ThemeColor,
		FrontSize:  req.FrontSize,
		DarkMode:   req.DarkMode,
		UseMode:    req.UseMode,
	})
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	c.JSON(http.StatusOK, resp.NewOkResp())
}

// UpdateUserInfoService 修改用户信息
func UpdateUserInfoService(c *gin.Context, id int, req *vo.UpdateUserInfoReq) {
	err := dao.UpdateUserInfoById(id, &entity.User{
		Nickname: req.NickName,
		Avatar:   req.Avatar,
	})
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误"))
		zap.L().Error(err.Error())
		return
	}
	c.JSON(http.StatusOK, resp.NewOkResp())
}

// UploadAvatarService 上传头像
func UploadAvatarService(c *gin.Context) {
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ParameterErrorCode, "参数不正确"))
		return
	}
	//生成文件名(文件md5.扩展名)
	r, _ := file.Open()
	md5h := md5.New()
	_, _ = io.Copy(md5h, r)
	fileName := fmt.Sprintf("%s%s", hex.EncodeToString(md5h.Sum(nil)), path.Ext(file.Filename))
	err = c.SaveUploadedFile(file, "./uploads/avatar/"+fileName)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误！"))
		zap.S().Error("保存头像错误: ", err)
		return
	}
	err = upyun.UploadAvatar(fileName)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误！"))
		zap.S().Error("上传头像错误: ", err)
		return
	}
	_ = os.Remove("./uploads/avatar/" + fileName)
	c.JSON(http.StatusOK, vo.NewUploadAvatarResp(fmt.Sprintf("%s/img/avatar/%s", setting.Conf.CDNDomain, fileName)))
}

// GetUserChannelsService 获取用户关注频道
func GetUserChannelsService(c *gin.Context, userId int) {
	channels, err := dao.GetChannelsByUserId(userId)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误！"))
		zap.S().Error("获取用户关注频道错误: ", err)
		return
	}
	c.JSON(http.StatusOK, vo.NewGetUserChannelsResp(channels))
}

// GetUserFavouriteNewsService 获取用户收藏新闻列表
func GetUserFavouriteNewsService(c *gin.Context, userId int) {
	p := utils.NewPagination(c)
	news, err := dao.GetUserFavouriteNewsById(userId, p)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误！"))
		zap.S().Error("获取用户收藏新闻列表错误: ", err)
		return
	}
	c.JSON(http.StatusOK, vo.NewGetUserNewsListResp(news, p))
}

// GetUserLikesNewsService 获取用户点赞新闻列表
func GetUserLikesNewsService(c *gin.Context, userId int) {
	p := utils.NewPagination(c)
	news, err := dao.GetUserLikesNewsById(userId, p)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误！"))
		zap.S().Error("获取用户点赞新闻列表错误: ", err)
		return
	}
	c.JSON(http.StatusOK, vo.NewGetUserNewsListResp(news, p))
}

// GetUserReportService 获取用户使用报告
func GetUserReportService(c *gin.Context, userId int) {
	report, err := dao.GetUseReportById(userId)
	if err != nil {
		c.JSON(http.StatusOK, resp.NewErrResp(consts.ServerErrorCode, "服务器错误！"))
		zap.S().Error("获取用户获取用户使用报告错误: ", err)
		return
	}
	c.JSON(http.StatusOK, vo.NewGetReportResp(report))
}
